import { App, ComponentPublicInstance, computed, defineComponent, ExtractPropTypes, PropType, provide, ref } from 'vue'
import { dataTableContextKey, RowData, TableColumn } from './interface'

import TableHeader from './TableHeader.vue'
import TableBody from './TableBody'

const tableProps = {
  prefixCls: { default: 'x-table' },
  columns: Array as PropType<TableColumn[]>,
  data: Array as PropType<RowData[]>,
  rowProps: Function as PropType<(rowData: RowData, rowIndex: number) => object>,
  maxHeight: Number,
  rowClass: [String, Function] as PropType<string | ((rowData: RowData, rowIndex: number) => string)>,
  bordered: Boolean,
  bottomBordered: { default: true },
  scrollX: Number,
  virtualScroll: Boolean,
  size: { type: String as PropType<'small' | 'medium' | 'large'>, default: 'medium' }
}

export type TableProps = ExtractPropTypes<typeof tableProps>

const Table = defineComponent({
  name: 'x-table',
  props: tableProps,
  setup(props, { slots }) {
    const headerRef = ref<ComponentPublicInstance>()
    const bodyRef = ref<ComponentPublicInstance>()

    const leftFixedColumsRef = computed(() => props.columns?.filter(col => col.fixed === 'left'))
    const rightFixedColumsRef = computed(() => props.columns?.filter(col => col.fixed === 'right'))

    const fixedColumnLeftMapRef = computed(() => {
      let left = 0
      return leftFixedColumsRef.value.reduce((map, col) => ((map[col.key] = left), (left += col.width || 0), map), {})
    })
    const fixedColumnRightMapRef = computed(() => {
      let right = 0
      return rightFixedColumsRef.value.reverse().reduce((map, col) => ((map[col.key] = right), (right += col.width || 0), map), {})
    })

    // ========================== Scroll ==========================
    let lastLeft = 0
    function onHeaderScroll(e: Event) {
      syncScroll(e, true)
    }
    function onBodyScroll(e: Event) {
      syncScroll(e, false)
    }
    function syncScroll(e: Event, header: boolean) {
      if ((e.target as HTMLElement).scrollLeft == lastLeft) return

      lastLeft = (e.target as HTMLElement).scrollLeft
      if (header) {
        ;(bodyRef.value.$el as HTMLElement).scrollLeft = lastLeft
      } else {
        if (!headerRef.value) return
        ;(headerRef.value.$el as HTMLElement).scrollLeft = lastLeft
      }
    }

    provide(dataTableContextKey, {
      props,
      leftFixedColumsRef,
      rightFixedColumsRef,
      fixedColumnLeftMapRef,
      fixedColumnRightMapRef
    })

    return () => {
      const { prefixCls, bordered, bottomBordered } = props

      const cls = [prefixCls, bordered && `${prefixCls}-bordered`, bottomBordered && `${prefixCls}-bottom-bordered`]

      return (
        <div class={cls}>
          <TableHeader ref={headerRef} onScroll={onHeaderScroll} />
          <TableBody ref={bodyRef} onScroll={onBodyScroll} v-slots={slots} />
        </div>
      )
    }
  }
})

Table.install = (app: App) => {
  app.component(Table.name, Table)
}

export default Table
